home *** CD-ROM | disk | FTP | other *** search
- page 59,120
- title RX50DRVR -- RX50 Diskette Driver for IBM PC-AT
- ;-----------------------------------------------------------------------;
- ; ;
- ; RX50DRVR -- RX50 Diskette Driver for IBM PC-AT ;
- ; ;
- ;-----------------------------------------------------------------------;
- VERSION equ 05 ; 08-Dec-84 15:10
-
- ; Copyright (c) 1984 by Robert F. Morse
- ; 17 Bowdoin Street
- ; Cambridge, MA 02138
- ;
- ; This is an MS-DOS loadable block device driver to support reading and
- ; writing DEC RX50 diskettes in the high capacity diskette drive of an
- ; IBM PC-AT. It uses the drive which is known to the normal PC-DOS as
- ; drive A: but designates it by the letter assigned when this driver is
- ; loaded. Needless to say, attempting to use both letters at the same
- ; time will lead to unpredictable results.
-
- .286C ;enable 186/286 instructions
-
- NUNITS equ 1 ;number of units supported by this driver
- PHYS_DRIVE_0 equ 00h ;drive number for the HC drive A:
- PHYS_BLKSIZE equ 512 ;blocks always 512 bytes
-
- DOSRUPT equ 21h
-
- CR equ 0Dh
- LF equ 0Ah
- ;---------------------------------------------------------------
- ; IBM ROM BIOS Definitions
- ;---------------------------------------------------------------
-
- DKOP_RUPT equ 013h ;interrupt to call ROM BIOS
- DKOP_RESET equ 000h ; reset controller
- DKOP_STATUS equ 001h ; read status from last operation
- DKOP_READ equ 002h ; read sectors
- DKOP_WRITE equ 003h ; write sectors
- DKOP_VERIFY equ 004h ; verify sectors
- DKOP_CHANGE equ 016h ; test changed status
- DKOP_SETTYPE equ 017h ; set media type in drive
-
- DKST_TIMEOUT equ 080h ;drive not ready
- DKST_BADSEEK equ 040h ;seek failed
- DKST_BADNEC equ 020h ;NEC controller failed
- DKST_BADCRC equ 010h ;read CRC error
- DKST_BADDMA equ 009h ;attempt to DMA over 64K boundary
- DKST_OVERRUN equ 008h ;DMA overrun
- DKST_CHANGED equ 006h ;media changed
- DKST_RNF equ 004h ;sector not found
- DKST_WRPROT equ 003h ;write-protected diskette
- DKST_ADRMARK equ 002h ;address mark not found
- DKST_BADCMD equ 001h ;invalid command
-
-
- BIOS_DATA_SEG equ 0040h
-
- BIOSDATA segment at BIOS_DATA_SEG ;BIOS data segment--
-
- org 0090h
- bios_dsk_state db ? ;drive 0 media state
-
- BIOS_DSK_360K equ 074h ; 360kb media established
- BIOS_DSK_RX50 equ 054h ; RX50 media established in drive
- ; (same as 360kb except single steps
- ; for 96 tpi media)
- BIOSDATA ends
- ;---------------------------------------------------------------
- ; I/O Request Packet Definition
- ;---------------------------------------------------------------
-
- iop_struc struc ;I/O request packet
- iop_len db ? ; packet length
- iop_unit db ? ; block device unit number
- iop_cmd db ? ; command code, 0..IOP_CMD_MAX
- iop_status dw ? ; status word, see IOPST... tags
- dd ? ; future queue link 1
- dd ? ; future queue line 2
-
- iop_media db ? ; media descriptor (or read-ahead byte)
- iop_bufoff dw ? ; buffer pointer offset
- iop_bufseg dw ? ; and segment
- iop_count dw ? ; byte/sector count
- iop_block dw ? ; starting block number
- iop_1stdrv db ? ; number of first block drive
- iop_struc ends
-
- iop_nextchar equ byte ptr iop_media ;next available input byte
- iop_nunits equ byte ptr iop_media ;number of units
- iop_bufptr equ dword ptr iop_bufoff ;buffer pointer
- iop_return equ byte ptr iop_bufoff ;disk-changed return code
- iop_ckvoloff equ word ptr iop_bufoff+1 ;pointer to
- iop_ckvolseg equ word ptr iop_bufoff+3 ; volume name for MEDIACHK
- iop_bpboff equ word ptr iop_count ;pointer to
- iop_bpbseg equ word ptr iop_count+2 ; BPB or BPB list
- iop_endoff equ word ptr iop_bufoff ;pointer to end of
- iop_endseg equ word ptr iop_bufoff+2 ; resident part of driver
- iop_rwvoloff equ word ptr iop_block+2 ;pointer to
- iop_rwvolseg equ word ptr iop_block+4 ; volume name for READ/WRITE
-
-
- IOP_CMD_MAX equ 15 ;highest valid command number
-
-
- IOPST_BUSY equ 0200h ;"busy" status
- IOPST_DONE equ 0100h ;"done" status
-
- IOPST_ERR equ 8000h ;"error" status, in combination with--
- IOPST_WRPROT equ 0000h ; write-protect
- IOPST_BADUNIT equ 0001h ; invalid unit number
- IOPST_NOTRDY equ 0002h ; unit not ready
- IOPST_BADCMD equ 0003h ; invalid command
- IOPST_CRC equ 0004h ; CRC error
- IOPST_BADIOP equ 0005h ; bad IOP length
- IOPST_SEEK equ 0006h ; seek error
- IOPST_UNKMEDIA equ 0007h ; unknown media
- IOPST_RNF equ 0008h ; sector not found
- IOPST_NOPAPER equ 0009h ; printer out of paper
- IOPST_WRFAULT equ 000Ah ; write fault
- IOPST_RDFAULT equ 000Bh ; read fault
- IOPST_IOERR equ 000Ch ; general I/O failure
- IOPST_BADCHNG equ 000Fh ; invalid diskette change
- ;---------------------------------------------------------------
- ; Bios Parameter Block Definition
- ;---------------------------------------------------------------
-
- bpb_struc struc
- bpb_sectsiz dw ? ; sector size, in bytes
- bpb_sectalu db ? ; sectors per allocation unit
- bpb_reserved dw ? ; number of reserved sectors before 1st FAT
- bpb_numfats db ? ; number of FAT's
- bpb_dirents dw ? ; number of root directory entries
- bpb_totsects dw ? ; total number of sectors on disk
- bpb_media db ? ; media descriptor byte
- bpb_fatsects dw ? ; number of sectors in a FAT
- ; additional fields to describe media:
- bpb_sect_trk dw ? ; sectors per track
- bpb_head_cyl dw ? ; heads per cylinder
- bpb_hidden dw ? ; number of "hiden" sectors
- bpb_struc ends
- ;=======================================================================;
- ; Permanently Resident Code & Data ;
- ;=======================================================================;
-
- DRVRSEG segment word
-
-
- ;-------- I/O Device Header Block --------
- ;
- ;This is at the first byte of the driver's code image.
-
- BLKDEV equ 0000h ;block device
- NONIBM equ 2000h ;non-IBM placement of FAT
- REMOVE equ 0800h ;has removable media
-
- header_rx50 label word
- dd -1
- dw BLKDEV+NONIBM+REMOVE
- dw offset strategy
- dw offset service
- db NUNITS, 0,0,0,0,0,0,0
-
- ;-------- Bios Parameter Blocks --------
-
- init_bpblist dw NUNITS dup (offset bpb_rx50)
- bpb_rx50 bpb_struc <512, 1, 20, 2, 96, 800, 0FAh, 3, 10, 1, 0>
- ;-------- Working Storage --------
-
- even
- io_packet_ptr dd ? ;save cell for IOP pointer
- drive_letter db 'A' ;letter for first drive supported
- vol_name db 'RX50DISK',0
- bpb_pointer dw 0 ;pointer to currently valid BPB
- open_count dw 0 ;count of open files on device
-
- ;items for sector transfer loop:
- xx_oper db ? ; operation code
- xx_count dw ? ; block counter
- xx_block dw ? ; block number
- xx_buf dd ? ; buffer pointer:
- xx_offset equ word ptr xx_buf ; offset
- xx_seg equ word ptr xx_buf+2 ; and segment
- xx_status db ? ; diskette status code
- xx_retries db ? ; error retry counter
-
-
- ;---------------------------------------------------------------
- ; IO_STRATEGY: strategy routine
- ;---------------------------------------------------------------
- ;
- ;Simple strategy routine which merely saves the I/O packet pointer
- ;passed in ES:BX and returns.
-
-
-
- assume cs:DRVRSEG, ds:nothing
- strategy proc far
- mov word ptr cs:io_packet_ptr, bx
- mov word ptr cs:io_packet_ptr+2, es
- ret
- strategy endp
- ;---------------------------------------------------------------
- ; IO_SERVICE: I/O request service routine
- ;---------------------------------------------------------------
- ;
- ;Entry point to command dispatcher for all service requests. Validates
- ;the command and invokes the command execution routine with ES:DI
- ;pointing to the I/O packet.
-
- assume cs:DRVRSEG, ds:nothing, es:nothing
- service proc far
- pusha ;save caller's registers
- push ds
- push es
- cld ;be sure of direction
-
- mov ax, cs ;set up local data segment
- mov ds, ax
- assume ds:DRVRSEG
- les di, io_packet_ptr ;set ES:DI to I/O packet
-
- mov al, es:iop_cmd[di] ;load command code from IOP
- cmp al, IOP_CMD_MAX ; and test its value
- ja invalid_command
- cbw ;convert to word index
- add ax, ax
- xchg bx, ax ;put vector index into BX
- xor ax, ax ;start with AX=0
- call drv_cmdtable [bx] ;call command executor
-
- ;Command routines return here with status flags in AX. Insert the
- ;"DONE" bit, store the status in the IOP, and exit from the driver.
-
- assume cs:DRVRSEG, ds:nothing
- epilogue:
- lds di, io_packet_ptr ;set DS:DI to IOP
- or ax, IOPST_DONE ;insert DONE flag
- mov ds:iop_status[di], ax ; and store status in IOP
-
- pop es ;restore caller's regs
- pop ds
- popa
- ret
- service endp
- ;-------- Command dispatch table --------
-
- even
- drv_cmdtable label word
- dw init ; 0 -- initialize
- dw mediachk ; 1 -- media check
- dw makebpb ; 2 -- build BPB
- dw cmd_dummy ; 3 -- IOCTL input
- dw read ; 4 -- read sectors
- dw cmd_dummy ; 5 -- peek at next byte
- dw cmd_dummy ; 6 -- test input status
- dw cmd_dummy ; 7 -- flush input buffer
- dw write ; 8 -- write sectors
- dw writver ; 9 -- write and verify sectors
- dw cmd_dummy ;10 -- test output status
- dw cmd_dummy ;11 -- flush output buffer
- dw cmd_dummy ;12 -- IOCTL output
- dw open ;13 -- device open
- dw close ;14 -- device close
- dw removable ;15 -- removable media check
-
-
- ;-------- Invalid command --------
- ;
- ;Load error status and jump to epilogue to return.
-
- invalid_command:
- mov ax, IOPST_ERR + IOPST_BADCMD
- jmp epilogue
-
-
- ;-------- Dummy command --------
- ;
- ;Perform no operation and return with OK status.
-
- cmd_dummy:
- xor ax, ax ;no errors
- ret
- ;---------------------------------------------------------------
- ; MEDIACHK -- Check for media change
- ;---------------------------------------------------------------
- ;
- ;Test whether the diskette has been changed. If this is the first
- ;use of the unit or if the BPB pointer is 0 (indicating a non-DOS
- ;diskette), always reply CHANGED to force a new BPB build.
- ;Otherwise, call the IBM BIOS to test the disk drive's DISKETTE
- ;CHANGE line and respond CHANGED or NOCHANGE accordingly.
-
- NOCHANGE equ 1
- DONTKNOW equ 0
- CHANGED equ -1
-
- assume cs:DRVRSEG, ds:DRVRSEG
- mediachk:
- mov si, bpb_pointer ;load pointer to BPB
- test si, si
- jz mediachk_changed ;reply CHANGED if non-DOS disk
-
- ;Call IBM ROM BIOS to get change status from drive.
-
- mov dl, PHYS_DRIVE_0 ;load drive number
- mov ah, DKOP_CHANGE ;BIOS op-code
- int DKOP_RUPT
- cmp ah, 00h
- je mediachk_nochange
- mediachk_changed:
- mov dl, CHANGED ;reply "changed"
- xor ax, ax ; with no error code
- mov open_count, ax ;zero count of open files
- jmp short mediachk_result
- mediachk_nochange:
- mov dl, NOCHANGE ;reply "no change"
- xor ax, ax ; with no error code
- mediachk_result:
- les di, io_packet_ptr ;get I/O packet pointer
- mov es:iop_ckvoloff[di], offset vol_name
- mov es:iop_ckvolseg[di], cs
- mov es:iop_return[di], dl ;store change return code
- ret
- ;---------------------------------------------------------------
- ; MAKEBPB -- Set up BPB
- ;---------------------------------------------------------------
- ;
- ;Set the ROM BIOS state for the drive to handle RX50 media and
- ;return a pointer to the RX50 BPB.
-
- assume cs:DRVRSEG, ds:DRVRSEG
- makebpb:
- mov open_count, 0 ;zero count of opened files
-
- mov dl, PHYS_DRIVE_0 ;load drive number
- mov al, 02h ;set up 360KB in 1.2m drive
- mov ah, DKOP_SETTYPE
- int DKOP_RUPT ;call BIOS to set up type
- mov xx_status, ah ; and store returned status
-
- test ah, DKST_TIMEOUT ;test for drive not ready
- jz makebpb_01 ;skip if no error
-
- mov ax, IOPST_ERR+IOPST_NOTRDY
- jmp short makebpb_ret
- makebpb_01:
- mov ax, BIOS_DATA_SEG ;change diskette status to
- mov es, ax ; single track stepping for 96 tpi
- mov es:bios_dsk_state, BIOS_DSK_RX50
-
- mov si, offset bpb_rx50 ;set up RX50 BPB
- mov bpb_pointer, si ;save address of active BPB
-
- les di, io_packet_ptr ;point to I/O packet
- mov al, bpb_media[si] ;get media descriptor byte
- mov es:iop_media[di], al ; and put into IOP
- mov es:iop_bpboff[di], si ;put BPB pointer
- mov es:iop_bpbseg[di], ds ; into IOP
- xor ax, ax ;no error
- makebpb_ret:
- ret
- ;---------------------------------------------------------------
- ; READ and WRITE: transfer sectors
- ;---------------------------------------------------------------
-
- assume cs:DRVRSEG, ds:DRVRSEG
- read:
- mov al, DKOP_READ ;set up READ operation
- call do_readwrite ; and do it
- jmp short rwv_fini
-
- write:
- mov al, DKOP_WRITE ;set up WRITE operation
- call do_readwrite ; and do it
- jmp short rwv_fini
-
- writver:
- mov al, DKOP_WRITE ;set up WRITE operation
- call do_readwrite ; and do it
- test ax, ax ;if there is an error
- jnz rwv_fini ; then quit now
-
- les di, io_packet_ptr ;reload address of I/O packet
- mov al, DKOP_VERIFY ;set up VERIFY operation
- call do_readwrite ; and do it
-
- rwv_fini:
- les di, io_packet_ptr ;set ES:DI to I/O packet
- mov dx, xx_count ;load number of sectors NOT
- sub es:iop_count[di], dx ; transferred and adjust IOP count
-
- ret ;return with AX = error code
- ;Common routine for read, write and verify.
- ;
- ;Given: AL = operation code
- ; ES:DI = pointer to IOP, which contains
- ; iop_block = starting block number
- ; iop_bufptr = starting buffer address
- ; iop_count = number of blocks
- ;Returns: AX = IOP error code
- ; xx_count = number of requested blocks NOT transferred
-
- do_readwrite:
- mov es:iop_rwvoloff[di], offset vol_name
- mov es:iop_rwvolseg[di], cs
-
- mov xx_oper, al ;save operation code
-
- mov ax, es:iop_block[di] ;set starting block number
- mov xx_block, ax
- mov ax, es:iop_count[di] ;set block count
- mov xx_count, ax
- test ax, ax
- jz dorw_success ; quit if 0 sectors to do
-
- mov ax, es:iop_bufoff[di] ;set starting buffer offset
- mov xx_offset, ax ; and segment
- mov ax, es:iop_bufseg[di]
- mov xx_seg, ax
-
- dorw_loop:
- mov xx_retries, 5 ;set retry counter
- dorw_again:
- mov ax, BIOS_DATA_SEG ;set diskette status to single
- mov es, ax ; stepping for 96 tpi
- mov es:bios_dsk_state, BIOS_DSK_RX50
-
- mov ax, xx_block ;load block number
- call makechs_rx50 ; and convert to CHS
- mov dl, PHYS_DRIVE_0 ;set drive number
- mov ah, xx_oper ;operation code
- mov al, 1 ;transfer 1 sector
- les bx, xx_buf ;set ES:BX to buffer address
- int DKOP_RUPT ;invoke ROM BIOS to do it
- mov xx_status, ah ; and save returned status
- test ah, ah ;test for error
- jnz dorw_error ; break loop on error
-
- inc xx_block ;advance to next block
- add xx_offset, PHYS_BLKSIZE ;advance buffer pointer
- dec xx_count ;count blocks
- jnz dorw_loop ; and continue until done
- dorw_success:
- xor ax, ax ;set no-error code
- ret
- ;Analyze read/write errors and either make another attempt or
- ;set the error code and return.
-
- dorw_error:
- mov al, IOPST_NOTRDY
- test ah, DKST_TIMEOUT
- jnz dorw_giveup
-
- mov al, IOPST_SEEK
- test ah, DKST_BADSEEK
- jnz dorw_retry
-
- mov al, IOPST_IOERR
- test ah, DKST_BADNEC
- jnz dorw_retry
-
- cmp ah, DKST_OVERRUN
- je dorw_retry
-
- mov al, IOPST_CRC
- cmp ah, DKST_BADCRC
- je dorw_retry
-
- mov al, IOPST_BADCMD
- cmp ah, DKST_BADDMA
- je dorw_giveup
-
- cmp ah, DKST_BADCMD
- je dorw_giveup
-
- mov al, IOPST_BADCHNG
- cmp ah, DKST_CHANGED
- jne dorw_nochange
- cmp open_count, 0
- jg dorw_giveup ;error if change with any files open
- jmp short dorw_reset ; else do it again
- dorw_nochange:
- mov al, IOPST_RNF
- cmp ah, DKST_RNF
- je dorw_retry
-
- mov al, IOPST_WRPROT
- cmp ah, DKST_WRPROT
- je dorw_giveup
-
- mov al, IOPST_UNKMEDIA
- cmp ah, DKST_ADRMARK
- je dorw_retry
-
- mov al, IOPST_IOERR
- jmp short dorw_giveup
- dorw_retry:
- dec xx_retries ;count retries
- jle dorw_giveup
- dorw_reset:
- mov ah, DKOP_RESET ;reset the disk controller
- int DKOP_RUPT
- jmp dorw_again ; and try again
- dorw_giveup:
- mov ah, high IOPST_ERR ;complete the driver error return code
- ret
- ;-------- MAKECHS_RX50 --------
- ;
- ;Convert block number to cylinder, head, sector for RX50. For cylinders
- ;2 through 79, the sectors are interleaved 2:1.
- ;
- ;Given: AX = block number.
- ;Returns: CH = cylinder,
- ; DH = head,
- ; CL = sector.
- ;Destroys: DL, AX.
-
- makechs_rx50:
- cwd ;set up block in DX:AX
- div secpertrk_rx50 ;AX=track, DX=sector
- xchg bx, dx ;get sector into BX
- cmp al, 2 ;test if cyls 0 or 1
- jb makechs_rx50_a ; skip if so
- mov bl, interleave_rx50[bx] ; else interleave sectors
- makechs_rx50_a:
- inc bx ;shift to 1-origin sector #
- xchg dx, bx ;restore BX
- mov ch, al ;CH = cylinder
- mov dh, 0 ;DH = head
- mov cl, dl ;CL = sector
- ret
-
- secpertrk_rx50 dw 10 ;sectors per RX50 track
-
- interleave_rx50 db 0,2,4,6,8,1,3,5,7,9 ;0-origin sector interleave table
- ;---------------------------------------------------------------
- ; OPEN -- Device Open
- ;---------------------------------------------------------------
- ;
- ;Increment the count of opened files on the device.
-
- assume cs:DRVRSEG, ds:DRVRSEG
- open:
- inc open_count
- ret
-
-
- ;---------------------------------------------------------------
- ; CLOSE -- Device Close
- ;---------------------------------------------------------------
- ;
- ;Decrement the count of opened files on the device.
-
- assume cs:DRVRSEG, ds:DRVRSEG
- close:
- cmp open_count, 0
- jle close_1
- dec open_count
- close_1:
- ret
-
-
- ;---------------------------------------------------------------
- ; REMOVABLE -- Report that Media are Removable
- ;---------------------------------------------------------------
- ;
- ;Return with IOPST_BUSY = 0 to indicate that media can be removed
- ;from this device.
-
- assume cs:DRVRSEG, ds:DRVRSEG
- removable:
- ret
- ;=======================================================================;
- ; Initialization Code & Data ;
- ;=======================================================================;
- ;
- ;Code and data from here on are discarded after INIT is called.
-
- even
- end_permanent_code label byte
-
-
- init_msg_1 db CR,LF,'RX50DRVR version '
- db (VERSION / 10) + '0'
- db (VERSION mod 10) + '0'
- db ' Copyright (c) 1984 by Robert F. Morse'
- db CR,LF,' Loaded at '
- init_msg_seg db '0000'
- db '0 and refers to drive as '
- init_msg_ltr db 'A:'
- db CR,LF,'$'
- ;---------------------------------------------------------------
- ; INIT -- Driver Initialization
- ;---------------------------------------------------------------
-
- assume cs:DRVRSEG, ds:DRVRSEG
- init:
-
- ;Put the drive letter and the segment at which the drive has been loaded
- ;into the signon message and display it.
-
- mov al, es:iop_1stdrv[di] ;get number to designate 1st drive
- add drive_letter, al ; save it
- add init_msg_ltr, al ; put in message
-
- mov bx, offset init_msg_seg ;put driver's address into msg
- mov cx, 4
- mov dx, cs
- init_hex_loop:
- rol dx, 4 ;convert left digit of DX to
- mov al, dl ; hex and store at [BX]+
- and al, 0Fh
- add al, '0'
- cmp al, '9'
- jbe init_hex_loop_1
- add al, 'A'-'9'-1
- init_hex_loop_1:
- mov [bx], al
- inc bx
- loop init_hex_loop ;do 4 digits
-
- mov dx, offset init_msg_1 ;display completed msg
- mov ah, 09h
- int DOSRUPT
-
- ;Fill in IOP with number of units, pointer to list of BPB's, and
- ;pointer to end of permanent code.
-
- assume ds:nothing
- lds di, io_packet_ptr ;set DS:DI to I/O packet
-
- mov ds:iop_nunits[di], NUNITS
-
- mov ds:iop_bpboff[di], offset init_bpblist
- mov ds:iop_bpbseg[di], cs
-
- mov ds:iop_endoff[di], offset end_permanent_code
- mov ds:iop_endseg[di], cs
-
- xor ax, ax ;return no error
- ret
-
- DRVRSEG ends
- end
-